﻿//========================================================= 
/**@file FolderHelper.h
 * @brief 文件夹工具类
 * 
 * @date 2014-06-01   17:09:02 
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_FOLDERHELPER_H_
#define _LIBZHOUYB_FOLDERHELPER_H_
//--------------------------------------------------------- 
#include "../../include/Base.h"

#include <fstream>

#ifndef _MSC_VER
#   include <sys/stat.h>
#   include <dirent.h>
#endif
//--------------------------------------------------------- 
/// 文件大小基数 
#define FILE_SIZE_MODE (1024)
/// n K
#define FILE_K(n) ((n)*FILE_SIZE_MODE)
/// n M
#define FILE_M(n) (FILE_K(n)*FILE_SIZE_MODE)
/// n G
#define FILE_G(n) (FILE_M(n)*FILE_SIZE_MODE)
/// n T
#define FILE_T(n) (FILE_G(n)*FILE_SIZE_MODE)
//--------------------------------------------------------- 
namespace zhou_yb {
namespace extension {
namespace ability {
//--------------------------------------------------------- 
/**
 * @brief 文件操作辅助类
 * @date 2018-08-01 16:06
 */
class FileHelper
{
protected:
    FileHelper() {}
public:
    /// 读取整个文件中的数据到字符串
    static string ReadToEnd(const char* path)
    {
        std::ifstream fin;
        fin.open(path, std::ios::in);
        if(fin.fail())
            return "";
        string str;
        std::getline(fin, str, static_cast<char>(EOF));
        return str;
    }
    /// 读取全部数据
    static void ReadAll(ifstream& ifs, ByteBuilder& data)
    {
        char buff[256];
        while (!ifs.eof())
        {
            ifs.read(buff, sizeof(buff));
            size_t count = static_cast<size_t>(ifs.gcount());
            if (count > 0 && count <= sizeof(buff))
            {
                data.Append(ByteArray(buff, count));
            }
        }
    }
    /// 读取指定大小的数据,返回实际读取到的长度
    static bool Read(ifstream& ifs, ByteBuilder& data, size_t count)
    {
        char buff[256];
        size_t read_count = 0;
        while (read_count < count)
        {
            size_t expect_count = _min(count - read_count, sizeof(buff));
            ifs.read(buff, expect_count);
            if (ifs.gcount() != expect_count)
            {
                data.RemoveTail(read_count);
                return false;
            }
            data.Append(ByteArray(buff, expect_count));
            read_count += expect_count;
        }
        return true;
    }
    /// 将数据整个写入到文件中
    static bool WriteToEnd(const char* path, const ByteArray& buff)
    {
        std::ofstream fout;
        fout.open(path, std::ios::out);
        if(fout.fail())
            return false;
        fout.write(buff.GetString(), buff.GetLength());
        return true;
    }
#ifdef _WIN32
    /// 返回文件是否存在
    static bool IsExists(const char* path)
    {
        WIN32_FIND_DATA wfd;
        CharConverter cvt;
        HANDLE hFind = FindFirstFile(cvt.to_char_t(path), &wfd);
        if(hFind == INVALID_HANDLE_VALUE)
            return false;
        FindClose(hFind);
        return true;
    }
    /// 获取文件大小 
    static size_t GetSize(const char* path)
    {
        WIN32_FIND_DATA wfd;
        CharConverter cvt;
        HANDLE hFind = FindFirstFile(cvt.to_char_t(path), &wfd);
        if(hFind == INVALID_HANDLE_VALUE)
            return static_cast<size_t>(0);
        FindClose(hFind);
        return static_cast<size_t>(wfd.nFileSizeLow);
}
#else
    /// 返回文件是否存在
    static bool IsExists(const char* path)
    {
        struct stat st;
        return (stat(path, &st) == 0);
    }
    /// 获取文件大小
    static size_t GetSize(const char* path)
    {
        struct stat st;
        if(stat(path, &st) != 0)
            return static_cast<size_t>(0);
        return static_cast<size_t>(st.st_size);
    }
#endif
};
//--------------------------------------------------------- 
#ifdef _MSC_VER
//--------------------------------------------------------- 
/**
 * @brief 文件夹辅助工具,负责文件夹的创建,枚举子文件,获取子文件大小,删除文件
 */
class FolderHelper
{
protected:
    FolderHelper() {}
public:
    /// 返回文件夹是否存在 
    static bool IsFolderExist(const char* path)
    {
        CharConverter cvt;
        DWORD dw = GetFileAttributes(cvt.to_char_t(path));
        if (dw == INVALID_FILE_ATTRIBUTES)
            return false;
        return (dw & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
    }
    /// 创建文件夹 
    static bool CreateFolder(const char* path, uint mode = 0)
    {
        size_t len = _strlen(path);
        ByteArray pathArray(path, len);
        size_t splitCount = StringConvert::GetSplitFlagCount(pathArray, PATH_SEPARATOR);
        // 记录第一个开始新建的文件夹,以便没有全部创建成功的时候删除 
        string createDir;
        string dir;
        ByteBuilder tmp(8);
        CharConverter cvt;

        for (size_t i = 0; i <= splitCount; ++i)
        {
            tmp.Clear();
            StringConvert::Split(pathArray, tmp, i, PATH_SEPARATOR);
            dir += tmp.GetString();

            // 不以 \\ 结尾 
            if (!tmp.IsEmpty())
                dir += static_cast<byte>(PATH_SEPARATOR);
            else
                break;

            // 如果文件夹不存在则新建 
            if (!IsFolderExist(dir.c_str()))
            {
                // 是第一个创建的文件夹 
                if (createDir.length() < 1)
                {
                    createDir = dir;
                }
                // 创建失败,删除已经创建的文件夹 
                if (CreateDirectory(cvt.to_char_t(dir.c_str()), NULL) != TRUE)
                {
                    DeleteFolder(createDir.c_str());
                    return false;
                }
            }
        }

        return true;
    }
    /// 删除文件夹 
    static bool DeleteFolder(const char* path)
    {
        char file[MAX_PATH];
        CharConverter cvt;
        file[0] = 0;

        size_t len = _strlen(path);
        memcpy(file, path, len);
        file[len] = 0;
        if (file[len - 1] != PATH_SEPARATOR)
            file[len++] = PATH_SEPARATOR;
        file[len++] = '*';
        file[len] = 0;

        WIN32_FIND_DATA wfd;
        HANDLE hFind = FindFirstFile(cvt.to_char_t(file), &wfd);
        if (hFind == INVALID_HANDLE_VALUE)
            return false;

        do
        {
            string subpath;
            subpath = path;
            subpath += PATH_SEPARATOR;
            subpath += cvt.to_char(wfd.cFileName);

            if (wfd.cFileName[0] == '.')
            {
                continue;
            }
            // 去除只读属性 
            SetFileAttributes(cvt.to_char_t(subpath.c_str()), FILE_ATTRIBUTE_NORMAL);
            if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                // 递归删除子文件夹 
                DeleteFolder(subpath.c_str());
                RemoveDirectory(cvt.to_char_t(subpath.c_str()));
                continue;
            }
            // 普通文件直接删除 
            DeleteFile(cvt.to_char_t(subpath.c_str()));
        } while (FindNextFile(hFind, &wfd));
        RemoveDirectory(cvt.to_char_t(path));
        FindClose(hFind);

        return true;
    }
    /// 删除单独的文件 
    static bool EraseFile(const char* path)
    {
        CharConverter cvt;
        return ::DeleteFile(cvt.to_char_t(path)) == TRUE;
    }
    /// 枚举文件夹下的第一层文件 
    static size_t EnumFiles(const char* dir, list_t<string>& files, list_t<size_t>* pfilesize = NULL, const char* ext = "*.*")
    {
        char file[MAX_PATH];
        CharConverter cvt;
        file[0] = 0;
        strcpy(file, dir);
        if (_strlen(ext) > 0)
        {
            strcat(file, "\\");
            if (ext[0] == '.')
                strcat(file, "*");
            strcat(file, ext);
        }
        else
        {
            strcpy(file, "\\*");
        }

        WIN32_FIND_DATA wfd;
        size_t count = 0;

        HANDLE hFind = FindFirstFile(cvt.to_char_t(file), &wfd);
        if (hFind == INVALID_HANDLE_VALUE)
            return static_cast<size_t>(0);

        do
        {
            // 跳过文件夹 
            if (wfd.cFileName[0] == '.' || (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                continue;
            }
            files.push_back(cvt.to_char(wfd.cFileName));
            if (pfilesize != NULL)
            {
                pfilesize->push_back(static_cast<size_t>(wfd.nFileSizeLow));
            }
            ++count;
        } while (FindNextFile(hFind, &wfd));
        FindClose(hFind);
        return count;
    }
    /// 枚举文件夹下的所有子文件夹和文件
    static bool EnumAll(const char* dir, list_t<string>& dirs, list_t<string>& files)
    {
        CharConverter cvt;
        WIN32_FIND_DATA wfd;

        HANDLE hFind = FindFirstFile(cvt.to_char_t(dir), &wfd);
        if (hFind == INVALID_HANDLE_VALUE)
            return false;

        do
        {
            // 文件夹 
            if (wfd.cFileName[0] == '.' || (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                dirs.push_back(cvt.to_char(wfd.cFileName));
                continue;
            }
            files.push_back(cvt.to_char(wfd.cFileName));
        } while (FindNextFile(hFind, &wfd));
        FindClose(hFind);
        return true;
    }
};
/**
 * @brief 将日志重定向到一个文件夹中,按照日期分组,控制最大日志文件数和文件大小
 */
//--------------------------------------------------------- 
#else
//---------------------------------------------------------
/**
 * @brief 文件夹辅助工具,负责文件夹的创建,枚举子文件,获取子文件大小,删除文件
 */
class FolderHelper
{
protected:
    FolderHelper() {}
public:
    /// 返回文件夹是否存在
    static bool IsFolderExist(const char* path)
    {
        struct stat st;
        if (stat(path, &st) != 0)
            return false;
        return S_ISDIR(st.st_mode) != 0;
    }
    /// 创建文件夹
    static bool CreateFolder(const char* path, uint mode = 0766)
    {
        ByteArray pathArray(path);
        size_t splitCount = StringConvert::GetSplitFlagCount(pathArray, PATH_SEPARATOR);
        // 记录第一个开始新建的文件夹,以便没有全部创建成功的时候删除
        string createDir;
        string dir;
        ByteBuilder tmp(8);
        for (size_t i = 0; i <= splitCount; ++i)
        {
            tmp.Clear();
            StringConvert::Split(pathArray, tmp, i, PATH_SEPARATOR);
            dir += tmp.GetString();
            dir += static_cast<byte>(PATH_SEPARATOR);

            // 不以 / 结尾
            if (tmp.IsEmpty())
            {
                continue;
            }

            // 如果文件夹不存在则新建
            if (!IsFolderExist(dir.c_str()))
            {
                // 是第一个创建的文件夹
                if (createDir.length() < 1)
                {
                    createDir = dir;
                }
                // 创建失败,删除已经创建的文件夹
                if (mkdir(dir.c_str(), mode) != 0)
                {
                    DeleteFolder(createDir.c_str());
                    return false;
                }
            }
        }
        return true;
    }
    /// 删除文件夹
    static bool DeleteFolder(const char* path)
    {
        DIR *pDir = NULL;
        struct dirent *pDirent = NULL;

        pDir = opendir(path);
        if (pDir == NULL)
            return false;

        while ((pDirent = readdir(pDir)) != NULL)
        {
            // 跳过隐藏的目录
            if (pDirent->d_name[0] == '.')
            {
                continue;
            }
            string fullpath = path;
            fullpath += "/";
            fullpath += pDirent->d_name;

            struct stat st;
            if (stat(fullpath.c_str(), &st) == 0)
            {
                // 如果是文件夹则递归删除
                if (S_ISDIR(st.st_mode))
                {
                    DeleteFolder(fullpath.c_str());
                    rmdir(fullpath.c_str());
                    continue;
                }
                // 普通文件直接删除
                EraseFile(fullpath.c_str());
            }
        }
        closedir(pDir);
        return true;
    }
    /// 删除单独的文件
    static bool EraseFile(const char* path)
    {
        return remove(path) == 0;
    }
    /// 枚举指定目录下的所有文件
    static bool EnumAll(const char* dir, list_t<string>* pDirs, list_t<string>* pFiles)
    {
        DIR *pDir = NULL;
        struct dirent *pDirent = NULL;

        pDir = opendir(dir);
        if (pDir == NULL)
            return false;

        while ((pDirent = readdir(pDir)) != NULL)
        {
            // 跳过隐藏的目录
            if (pDirent->d_name[0] == '.')
                continue;

            string fullpath = dir;
            if (fullpath[fullpath.length() - 1] != PATH_SEPARATOR)
                fullpath += static_cast<byte>(PATH_SEPARATOR);
            fullpath += pDirent->d_name;

            struct stat st;
            if (stat(fullpath.c_str(), &st) == 0)
            {
                // 如果是文件夹则跳过
                if (S_ISDIR(st.st_mode))
                {
                    if (pDirs != NULL)
                    {
                        pDirs->push_back(pDirent->d_name);
                    }
                }
                else
                {
                    if (pFiles != NULL)
                    {
                        pFiles->push_back(pDirent->d_name);
                    }
                }
            }
        }
        closedir(pDir);

        return true;
    }
    /// 枚举文件夹下的第一层文件
    static size_t EnumFiles(const char* dir, list_t<string>& files, list_t<size_t>* pfilesize = NULL, const char* ext = "")
    {
        DIR *pDir = NULL;
        struct dirent *pDirent = NULL;
        size_t count = 0;

        pDir = opendir(dir);
        if (pDir == NULL)
            return static_cast<size_t>(0);

        while ((pDirent = readdir(pDir)) != NULL)
        {
            // 跳过隐藏的目录
            if (pDirent->d_name[0] == '.')
            {
                continue;
            }
            string fullpath = dir;
            if (fullpath[fullpath.length() - 1] != PATH_SEPARATOR)
                fullpath += static_cast<byte>(PATH_SEPARATOR);
            fullpath += pDirent->d_name;

            // 过滤文件
            if (StringConvert::EndWith(pDirent->d_name, _strput(ext), true) != true)
                continue;

            struct stat st;
            if (stat(fullpath.c_str(), &st) == 0)
            {
                // 如果是文件夹则跳过
                if (S_ISDIR(st.st_mode))
                    continue;
                files.push_back(pDirent->d_name);
                if (pfilesize != NULL)
                {
                    pfilesize->push_back(static_cast<size_t>(st.st_size));
                }
            }
        }
        closedir(pDir);

        return count;
    }
};
//--------------------------------------------------------- 
#endif
//--------------------------------------------------------- 
} // namespace ability 
} // namespace extension 
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_FOLDERHELPER_H_
//========================================================= 